#include <stdio.h>
#include <string.h>
#include "tcp_client.h"
#include "hal.h"
#include "infra_defs.h"
#include "tcp_client_cfg.h"
#include "infra_timer.h"


/* set state of tcp client */
void tc_set_client_state(tcp_client_t *pClient, tc_state_t newState)
{
    HAL_MutexLock(pClient->lock_generic);
    pClient->client_state = newState;
    HAL_MutexUnlock(pClient->lock_generic);   
}

tc_state_t tc_get_client_state(tcp_client_t *pclient)
{
    tc_state_t state = TC_STATE_INVALID;
    if(pclient == NULL){
        return TC_STATE_INVALID;
    }

    HAL_MutexLock(pclient->lock_generic);
    state = pclient->client_state;
    HAL_MutexUnlock(pclient->lock_generic);

    return state;
}

static int tcp_client_common_init(tcp_client_t *pclient, const char *host, uint16_t port,
                                const char *ca_crt,
                                const char *client_crt,
                                const char *client_key,
                                const char *client_pwd,
                                const char *CN_name)
{
    int rc = FAIL_RETURN;
    int len_tmp;
    tc_state_t tc_state = TC_STATE_INVALID;

    if (pclient == NULL || host == NULL) {
        return NULL_VALUE_ERROR; 
    }

    pclient->lock_generic = HAL_MutexCreate();
    if (pclient->lock_generic == NULL) {
        goto end;
    }

    pclient->lock_read_buf = HAL_MutexCreate();
    if (pclient->lock_read_buf == NULL) {
       goto end;
    }

    pclient->lock_write_buf = HAL_MutexCreate();
    if (pclient->lock_write_buf == NULL) {
        goto end;
    }

    // host
    len_tmp = strlen(host);
    pclient->buf_host = (char *)HAL_Malloc(len_tmp + 1);
    if (pclient->buf_host == NULL) {
        HAL_Printf("%s: malloc failed\n", __func__);
        goto end;
    }
    memset(pclient->buf_host, 0, len_tmp + 1);
    strncpy(pclient->buf_host, host, len_tmp);
    pclient->ipstack.pHostAddress = pclient->buf_host;
    // port 
    pclient->ipstack.port = port;
    // request timeout
    pclient->send_data_timeout_ms = CONFIG_TC_SEND_DATA_TIMEOUT;

    pclient->buf_size_read_max = CONFIG_TC_BUFFER_READ_MAX;
    pclient->buf_size_send_max = CONFIG_TC_BUFFER_SEND_MAX;
    pclient->keepalive_interval_ms = CONFIG_TC_KEEPALIVE_INTERVAL_MS;

    utils_time_init(&pclient->next_ping_time);
    utils_time_init(&pclient->reconnect_next_time);

    // network init
    memset(&pclient->ipstack, 0, sizeof(utils_network_t));
    rc = utils_net_init(&pclient->ipstack, host, port, ca_crt, client_crt, client_key, client_pwd, CN_name);
    if(rc != SUCCESS_RETURN){
        tc_state = TC_STATE_INVALID;
        HAL_Printf("utils_net_init failed\n");
        goto end;
    }

    tc_state = TC_STATE_INITIALIZED;
    rc = SUCCESS_RETURN;
    HAL_Printf("tcp_client_common_init success\n");
    
end:
    tc_set_client_state(pclient, tc_state);

    if(rc != SUCCESS_RETURN){
        if(pclient->buf_send){
            HAL_Free(pclient->buf_send);
            pclient->buf_send = NULL;
        }

        if(pclient->buf_read){
            HAL_Free(pclient->buf_read);
            pclient->buf_read = NULL;
        }

        if(pclient->buf_host){
            HAL_Free(pclient->buf_host);
            pclient->buf_host = NULL;
        }

        if(pclient->lock_write_buf){
            HAL_MutexDestroy(pclient->lock_write_buf);
            pclient->lock_write_buf = NULL;
        }

        if(pclient->lock_read_buf){
            HAL_MutexDestroy(pclient->lock_read_buf);
            pclient->lock_read_buf = NULL;
        }

        if(pclient->lock_generic){
            HAL_MutexDestroy(pclient->lock_generic);
            pclient->lock_generic = NULL;
        }
    }


    return rc;
}

tcp_client_t *tcp_client_new(const char *host, uint16_t port)
{
    int err;
    tcp_client_t *pclient = NULL;

    pclient = (tcp_client_t *)HAL_Malloc(sizeof(tcp_client_t));
    if (pclient == NULL) {
        HAL_Printf("%s: malloc failed\n", __func__);
        return NULL;
    }
    memset(pclient, 0, sizeof(tcp_client_t));
    err = tcp_client_common_init(pclient, host, port, NULL, NULL, NULL, NULL, NULL);
    if(err != SUCCESS_RETURN){
        HAL_Printf("tcp_client_common_init failed\n");
        HAL_Free(pclient);
        return NULL;
    }

    return pclient;
}

int tcp_client_connect(tcp_client_t *pclient)
{
    int rc = FAIL_RETURN;
    int retry_max = 3;
    int retry_cnt = 1;
    int retry_interval = 1000;
    
    if(pclient == NULL){
        return NULL_VALUE_ERROR;
    }

    if(pclient->client_state == TC_STATE_CONNECTED){
        HAL_Printf("tcp_client_connect: already connected\n");
        return SUCCESS_RETURN;
    }

    
    do{
        rc = pclient->ipstack.connect(&pclient->ipstack);
        if(rc != SUCCESS_RETURN){
            pclient->ipstack.disconnect(&pclient->ipstack);
            HAL_Printf("tcp_client_connect: connect failed, retry_cnt = %d\n", retry_cnt);
            HAL_SleepMs(retry_interval);
            continue;
        }else{
            HAL_Printf("tcp_client_connect: connect success, retry:%d\n", retry_cnt);
            break;
        }
    } while(++retry_cnt <= retry_max);

    if(rc == SUCCESS_RETURN){
        tc_set_client_state(pclient, TC_STATE_CONNECTED);

        if(pclient->tc_state_notify_cb){
            pclient->tc_state_notify_cb(pclient, TC_STATE_CONNECTED);
        }

        // 更新心跳间隔时间
        utils_time_countdown_ms(&pclient->next_ping_time, pclient->keepalive_interval_ms);
    }

    return rc;
}


int tcp_client_send(tcp_client_t *pclient, unsigned char *buf, unsigned int length)
{
    utils_time_t timer;
    int sent = 0;
    unsigned int left_t = 0;
    int rc = FAIL_RETURN;

    if(pclient == NULL || buf == NULL || length == 0){
        HAL_Printf("pclient or buf or buf_len is NULL\n");
        return 0;
    }

    utils_time_countdown_ms(&timer, pclient->send_data_timeout_ms);

    while(sent < length && !utils_time_is_expired(&timer)){
        left_t = utils_time_left(&timer);
        left_t = (left_t == 0) ? 1 : left_t;
        rc = pclient->ipstack.write(&pclient->ipstack, buf + sent, length - sent, left_t);
        if(rc < 0){
            break;
        }
        sent += rc;
    }

    if(sent == length){
        rc = SUCCESS_RETURN;
    }else{
        rc = ERROR_NETWORK_ERROR;
    }
    return rc;
}



static int alloc_recv_buffer(tcp_client_t *pclient, int len)
{
    int tmp_len;

    if(pclient == NULL){
        return FAIL_RETURN;
    }

    tmp_len = len;

    if(tmp_len > pclient->buf_size_read_max){
        tmp_len = pclient->buf_size_read_max;
    }

    if(pclient->buf_read != NULL){
        // do realloc
        char *temp = HAL_Malloc(tmp_len);
        if(temp == NULL){
            HAL_Printf("tcp_client_read_packet: malloc failed\n");
            return ERROR_MALLOC;
        }

        memset(temp, 0, tmp_len);
        memcpy(temp, pclient->buf_read, pclient->buf_size_read < tmp_len ? pclient->buf_size_read : tmp_len);
        HAL_Free(pclient->buf_read);
        pclient->buf_read = temp;
    }else{
        pclient->buf_read = HAL_Malloc(tmp_len);
        if(pclient->buf_read == NULL){
            HAL_Printf("tcp_client_read_packet: malloc failed\n");
            return ERROR_MALLOC;
        }
        memset(pclient->buf_read, 0, tmp_len);
    }

    pclient->buf_size_read = tmp_len;
    return SUCCESS_RETURN;
}


static int reset_recv_buffer(tcp_client_t *pclient)
{
    if(pclient == NULL){
        return FAIL_RETURN;
    }

    HAL_Free(pclient->buf_read);
    pclient->buf_read = NULL;
    pclient->buf_size_read = 0;
    return 0;
}


static int tcp_client_read_packet(tcp_client_t *pclient, utils_time_t *timer, unsigned int *packet_type)
{
    int len = 0;
    uint32_t rem_len = 0;
    int rc = FAIL_RETURN;
    unsigned int left_t = 0;
    int flag_need_unlock = 0;

    if(pclient == NULL || timer == NULL){
        return NULL_VALUE_ERROR;
    }

    HAL_MutexLock(pclient->lock_read_buf);
    flag_need_unlock = 1;
    // 1. 读取type，共2个字节
    rc = alloc_recv_buffer(pclient, CONFIG_TC_TLV_TYPE_LEN);
    if(rc < 0){
        goto end;
    }

    left_t = utils_time_left(timer);
    left_t = (left_t == 0) ? 1 : left_t;
    rc = pclient->ipstack.read(&pclient->ipstack, pclient->buf_read, CONFIG_TC_TLV_TYPE_LEN, left_t);
    if(rc == 0){
        *packet_type = CONFIG_TC_TLV_TYPE_ERROR;
        goto end;
    }else if(rc != CONFIG_TC_TLV_TYPE_LEN){
        HAL_Printf("tcp_client_read_packet: read type failed, rc = %d\n", rc);
        rc = ERROR_NETWORK_ERROR;
        goto end;
    }

    *packet_type = pclient->buf_read[0] << 8 | pclient->buf_read[1];

    len = CONFIG_TC_TLV_TYPE_LEN;

    // 读取 length 4个字节
    left_t = utils_time_left(timer);
    left_t = (left_t == 0) ? 1 : left_t;

    rc = alloc_recv_buffer(pclient, CONFIG_TC_TLV_TYPE_LEN + CONFIG_TC_TLV_LENGTH_LEN);
    if(rc < 0){
        goto end;
    }

    rc = pclient->ipstack.read(&pclient->ipstack, pclient->buf_read + len, CONFIG_TC_TLV_LENGTH_LEN, left_t);
    if(rc == 0){
        goto end;
    }else if(rc != CONFIG_TC_TLV_LENGTH_LEN){
        HAL_Printf("tcp_client_read_packet: read length failed, rc = %d\n", rc);
        rc = ERROR_NETWORK_ERROR;
        goto end;
    }

    len = len + CONFIG_TC_TLV_LENGTH_LEN;

    // 读取 length 个字节
    HAL_Printf("Debug recv length byte:[%X][%X][%X][%X]\n", pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN], 
                                        pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 1], 
                                        pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 2], 
                                        pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 3]);

    rem_len = pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN] << 24;
    rem_len |= pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 1] << 16;
    rem_len |= pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 2] << 8;
    rem_len |= pclient->buf_read[CONFIG_TC_TLV_TYPE_LEN + 3];

    HAL_Printf("Debug recv length:[%d]\n", rem_len);

    if(rem_len == 0){
        HAL_MutexUnlock(pclient->lock_read_buf);
        HAL_Printf("tcp_client_read_packet: read length is 0\n");
        return SUCCESS_RETURN;
    }

    pclient->buf_read_len = rem_len;


    // 判断剩余长度是否超过read buffer最大值
    if((len + rem_len) > pclient->buf_size_read_max){
        HAL_Printf("tcp_client_read_packet: read length too long, rem_len = %d\n", rem_len);
        
        int needReadLen;
        int needDropDataLen;
        char *needDropDataBuf;
        // 先将buf_read 读满
        needReadLen = pclient->buf_size_read_max - len;

        left_t = utils_time_left(timer);
        left_t = (left_t == 0) ? 1 : left_t;
        rc = alloc_recv_buffer(pclient, CONFIG_TC_TLV_TYPE_LEN + CONFIG_TC_TLV_LENGTH_LEN + needReadLen);
        if(rc < 0){
            goto end;
        }
        rc = pclient->ipstack.read(&pclient->ipstack, pclient->buf_read + len, needReadLen, left_t);

        // 剩余的数据也需要读取出来，然后丢掉，避免socket内有脏数据
        needDropDataLen = rem_len - needReadLen;
        needDropDataBuf = HAL_Malloc(needDropDataLen + 1);
        if(needDropDataBuf == NULL){
            HAL_Printf("malloc failed\n");
            rc = FAIL_RETURN;
            goto end;
        }
        memset(needDropDataBuf, 0, needDropDataLen + 1);

        left_t = utils_time_left(timer);
        left_t = (left_t == 0) ? 1 : left_t;
        rc = pclient->ipstack.read(&pclient->ipstack, needDropDataBuf, needDropDataLen, left_t);
        if(rc < 0){
            HAL_Printf("read failed\n");
            HAL_Free(needDropDataBuf);
            needDropDataBuf = NULL;
            rc = NETWORK_ADDR_LEN;
            goto end;
        }else if(rc != needDropDataLen){
            HAL_Printf("read timeout\n");
            HAL_Free(needDropDataBuf);
            needDropDataBuf = NULL;
            rc = FAIL_RETURN;
            goto end;
        }

        HAL_Free(needDropDataBuf);
        needDropDataBuf = NULL;

        HAL_MutexUnlock(pclient->lock_read_buf);
        return SUCCESS_RETURN;
    }
    
    // 将TLV中的'V' 读取
    rc = alloc_recv_buffer(pclient, CONFIG_TC_TLV_TYPE_LEN + CONFIG_TC_TLV_LENGTH_LEN + rem_len);
    if(rc < 0){
        HAL_Printf("alloc_recv_buffer failed\n");
        goto end;
    }

    left_t = utils_time_left(timer);
    left_t = (left_t == 0) ? 1 : left_t;
    rc = pclient->ipstack.read(&pclient->ipstack, pclient->buf_read + len, rem_len, left_t);
    if(rc < 0){
        HAL_Printf("read failed\n");
    }else if(rc != rem_len){
        HAL_Printf("read timeout\n");
    }

    rc = SUCCESS_RETURN;

end:
    if(flag_need_unlock){
        HAL_MutexUnlock(pclient->lock_read_buf);
    }
    return rc;
}


static int tcp_client_cycle(tcp_client_t *pclient, utils_time_t *timer)
{
    tc_state_t state;
    unsigned int packet_type;
    int rc = SUCCESS_RETURN;

    if(pclient == NULL){
        HAL_Printf("pclient is NULL\n");
        return FAIL_RETURN;
    }

    state = tc_get_client_state(pclient);
    if(state != TC_STATE_CONNECTED){
        //HAL_Printf("state is not connected\n");
        return FAIL_RETURN;
    }

    // 读取socket
    rc = tcp_client_read_packet(pclient, timer, &packet_type);
    if(rc != SUCCESS_RETURN){
        HAL_MutexLock(pclient->lock_read_buf);
        reset_recv_buffer(pclient);
        HAL_MutexUnlock(pclient->lock_read_buf);
        if(rc == ERROR_NETWORK_ERROR){
            tc_set_client_state(pclient, TC_STATE_DISCONNECTED);
        }
        HAL_Printf("tcp_client_read_packet failed, rc = %d\n", rc);
        return ERROR_NETWORK_ERROR;
    }

    if(packet_type == CONFIG_TC_TLV_TYPE_ERROR){
        // read data timeout
        HAL_MutexLock(pclient->lock_read_buf);
        reset_recv_buffer(pclient);
        HAL_MutexUnlock(pclient->lock_read_buf);
        return SUCCESS_RETURN;
    }


    HAL_MutexLock(pclient->lock_read_buf);
    switch(packet_type){
        case CONFIG_TC_TLV_TYPE_PING:
            HAL_Printf("tcp_client_read_packet: CONFIG_TC_TLV_TYPE_PING\n");
            if(pclient->tc_data_recv_cb){
                    //pclient->tc_data_recv_cb(pclient, packet_type, pclient->buf_read);
                    pclient->tc_data_recv_cb(pclient, packet_type, 
                                        &pclient->buf_read[6], 
                                        pclient->buf_read_len,
                                        pclient->tc_data_recv_user_data);
                }
        break;

        default:
            if(packet_type > 0){
                if(pclient->tc_data_recv_cb){
                    //pclient->tc_data_recv_cb(pclient, packet_type, pclient->buf_read);
                    pclient->tc_data_recv_cb(pclient, packet_type, 
                                        &pclient->buf_read[6], 
                                        pclient->buf_read_len, pclient->tc_data_recv_user_data);
                }
            }
            break;
    }

    reset_recv_buffer(pclient);
    HAL_MutexUnlock(pclient->lock_read_buf);

    return rc;
}


static int tcp_client_attempt_reconnect(tcp_client_t *pclient)
{
    int rc = FAIL_RETURN;
    if(pclient == NULL){
        HAL_Printf("pclient is NULL\n");
        return FAIL_RETURN;
    }

    pclient->ipstack.disconnect(&pclient->ipstack);

    rc = tcp_client_connect(pclient);
    if(rc < 0){
        HAL_Printf("tcp_client_connect failed\n");
    }

    return rc;
}

static int tcp_client_handle_reconnect(tcp_client_t *pclient)
{
    int rc = FAIL_RETURN;
    uint32_t interval_ms = 0;
    if(pclient == NULL){
        HAL_Printf("pclient is NULL\n");
        return FAIL_RETURN;
    }

    if(!utils_time_is_expired(&pclient->reconnect_next_time)){
        // 重连时间未到，等待重连timer
        HAL_SleepMs(100);
        return FAIL_RETURN;
    }

    HAL_Printf("start to reconnecting...\n");
    rc = tcp_client_attempt_reconnect(pclient);
    if(rc == SUCCESS_RETURN){
        tc_set_client_state(pclient, TC_STATE_CONNECTED);
        return SUCCESS_RETURN;
    }else{
        if(pclient->reconnect_time_interval_ms < CONFIG_TC_RECONNECT_INTERVAL_MAX_MS){
            pclient->reconnect_time_interval_ms *= 2;
        }else{
            pclient->reconnect_time_interval_ms = CONFIG_TC_RECONNECT_INTERVAL_MAX_MS;
        }
    }

    interval_ms = pclient->reconnect_time_interval_ms;
    if(interval_ms > CONFIG_TC_RECONNECT_INTERVAL_MAX_MS){
        interval_ms = CONFIG_TC_RECONNECT_INTERVAL_MAX_MS;
    }

    utils_time_countdown_ms(&pclient->reconnect_next_time, interval_ms);
    HAL_Printf("reconnect failed rc = %d\n", rc);

    return rc;
}

static int tcp_client_keepalive_heartbeat_send(tcp_client_t *pclient)
{
    int rc = SUCCESS_RETURN;
    tc_state_t currentState;
    uint8_t *buf_heartbeat;
    uint32_t buf_heartbeat_len;
    uint8_t buf[CONFIG_TC_TLV_TYPE_LEN + CONFIG_TC_TLV_LENGTH_LEN + 4] = {0};

    if(pclient == NULL){
        HAL_Printf("pclient is NULL\n");
        return FAIL_RETURN;
    }

    currentState = tc_get_client_state(pclient);
    if(currentState != TC_STATE_CONNECTED){
        return SUCCESS_RETURN;
    }

    // 检查是否需要发送心跳包
    if(!utils_time_is_expired(&pclient->next_ping_time)){
        return SUCCESS_RETURN;
    }

    // 更新下次发送心跳包时间
    utils_time_countdown_ms(&pclient->next_ping_time, CONFIG_TC_KEEPALIVE_INTERVAL_MS);

    // 申请一个更新的心跳包
    buf_heartbeat_len = HAL_tcp_client_heartbeat_packet_new(&buf_heartbeat);
    if(buf_heartbeat == NULL){
        HAL_Printf("buf_heartbeat is NULL\n");
        return FAIL_RETURN;
    }

    // 发送心跳包
    //rc = pclient->ipstack.write(&pclient->ipstack, buf_heartbeat, buf_heartbeat_len, 1000);

    rc = tcp_client_send(pclient, buf_heartbeat, buf_heartbeat_len);

    if(rc != SUCCESS_RETURN){
        HAL_Printf("write heartbeat failed\n");
        if(rc == ERROR_NETWORK_ERROR){
            tc_set_client_state(pclient, TC_STATE_DISCONNECTED);
        }
    }
    // 释放心跳包buf
    HAL_Free(buf_heartbeat);

    return SUCCESS_RETURN;
}

static void tcp_client_keepalive(tcp_client_t *pclient)
{
    int rc = 0;
    tc_state_t currentState;
    if(pclient == NULL){
        HAL_Printf("pclient is NULL\n");
        return;
    }

    // 发送心跳数据
    tcp_client_keepalive_heartbeat_send(pclient);

    currentState = tc_get_client_state(pclient);
    do{
        if(currentState == TC_STATE_DISCONNECTED_RECONNECTING){
            rc = tcp_client_handle_reconnect(pclient);

            if(rc == SUCCESS_RETURN){
                HAL_Printf("network is reconnected.\n");
                pclient->reconnect_time_interval_ms = CONFIG_TC_RECONNECT_INTERVAL_MIN_MS;
            }
            break;
        }

        if(currentState == TC_STATE_DISCONNECTED){
           HAL_Printf("network is disconnected.\n"); 
           if(pclient->tc_state_notify_cb){
               pclient->tc_state_notify_cb(pclient, TC_STATE_DISCONNECTED);
           }
           
           pclient->reconnect_time_interval_ms = CONFIG_TC_RECONNECT_INTERVAL_MIN_MS;
           utils_time_countdown_ms(&pclient->reconnect_next_time, pclient->reconnect_time_interval_ms);

           pclient->ipstack.disconnect(&pclient->ipstack);
           tc_set_client_state(pclient, TC_STATE_DISCONNECTED_RECONNECTING);
           break;
        }

    }while(0);
}





void tcp_client_yield(tcp_client_t *pclient, int timeout_ms)
{
    int rc = SUCCESS_RETURN;
    utils_time_t timer;

    if(timeout_ms <= 0){
        timeout_ms = 10;
    }

    utils_time_init(&timer);
    utils_time_countdown_ms(&timer, timeout_ms);

    tcp_client_keepalive(pclient);

    do{
        unsigned int left_t;
        rc = tcp_client_cycle(pclient, &timer);
        left_t = utils_time_left(&timer);
        if(left_t < 10){
            HAL_SleepMs(left_t);
        }else{
            HAL_SleepMs(10);
        }
    }while(!utils_time_is_expired(&timer)); 
}


int tcp_client_set_state_notify_cb(tcp_client_t *pclient, tc_state_notify_callback_fpt cb)
{
    if(pclient == NULL || cb == NULL){
        HAL_Printf("pclient or cb is NULL\n");
        return FAIL_RETURN;
    }

    pclient->tc_state_notify_cb = cb;

    return SUCCESS_RETURN;

}


int tcp_client_set_data_recv_cb(tcp_client_t *pclient, tc_data_recv_callback_fpt cb, void *user_data)
{
    if(pclient == NULL || cb == NULL){
        HAL_Printf("pclient or cb is NULL\n");
        return FAIL_RETURN;
    }

    pclient->tc_data_recv_cb = cb;
    pclient->tc_data_recv_user_data = user_data;
    return SUCCESS_RETURN;
}


